const querystring = require('querystring');
const REQUEST = require('request');
const util = require('./util');
const config = require('./config');
const fs = require('fs');


// ---------------------------------------- Bucket 相关 api ------------------------------------

/**
 * 获取用户的 bucket 列表
 * @param  {Object}   callback   回调函数，必须，下面为参数列表
	 * 无特殊参数
 */
function getService(params, callback) {
  return submitRequest({
    url: 'http://service.cos.myqcloud.com',
    method: 'GET',
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      return callback(err);
    }

    data = data || {};

    if (data && data.ListAllMyBucketsResult
			&& data.ListAllMyBucketsResult.Buckets
			&& data.ListAllMyBucketsResult.Buckets.Bucket) {
      let buckets = data.ListAllMyBucketsResult.Buckets.Bucket || [];

      if (!(buckets instanceof Array)) {
        buckets = [ buckets ];
      }

      data.ListAllMyBucketsResult.Buckets = buckets;
    }

    return callback(null, data.ListAllMyBucketsResult);
  });
}

/**
 * 查看是否存在该Bucket，是否有权限访问
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	 	请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data 	返回的数据
	 * @return  {Boolean}  	data.BucketExist 	Bucket是否存在
	 * @return  {Boolean}	data.BucketAuth	是否有 Bucket 的访问权限
 */
function headBucket(params, callback) {
  return submitRequest({
    Bucket: params.Bucket,
    Region: params.Region,
    method: 'HEAD',
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, body) {
    if (err) {
      const statusCode = err.statusCode;
      if (statusCode && statusCode == 404) {
        return callback(null, {
          BucketExist: false,
          BucketAuth: false,
        });
      } else if (statusCode && statusCode == 403) {
        return callback(null, {
          BucketExist: true,
          BucketAuth: false,
        });
      }
      return callback(err);

    }

    return callback(null, {
      BucketExist: true,
      BucketAuth: true,
    });
  });
}

/**
 * 获取 Bucket 下的 object 列表
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
	 * @param  {string}   params.Prefix     前缀匹配，用来规定返回的文件前缀地址，非必须
	 * @param  {string}   params.Delimiter       定界符为一个符号，如果有Prefix，则将Prefix到delimiter之间的相同路径归为一类，非必须
	 * @param  {string}   params.Marker       默认以UTF-8二进制顺序列出条目，所有列出条目从marker开始，非必须
	 * @param  {string}   params.MaxKeys       单次返回最大的条目数量，默认1000，非必须
	 * @param  {string}   params.EncodingType       规定返回值的编码方式，非必须
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	 请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data 返回的数据
	 * @return  {object}	data.ListBucketResult 	返回的 object 列表信息
 */
function getBucket(params, callback) {
  const reqParams = {};
  reqParams.prefix = params.Prefix;
  reqParams.delimiter = params.Delimiter;
  reqParams.marker = params.Marker;
  reqParams['max-keys'] = params.MaxKeys;
  reqParams['encoding-type'] = params.EncodingType;

  return submitRequest({
    method: 'GET',
    Bucket: params.Bucket,
    Region: params.Region,
    qs: reqParams,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      return callback(err);
    }

    data = data || {};

    let contents = data.ListBucketResult.Contents || [];
    let CommonPrefixes = data.ListBucketResult.CommonPrefixes || [];

    if (!(contents instanceof Array)) {
      contents = [ contents ];
    }

    if (!(CommonPrefixes instanceof Array)) {
      CommonPrefixes = [ CommonPrefixes ];
    }

    data.ListBucketResult.Contents = contents;
    data.ListBucketResult.CommonPrefixes = CommonPrefixes;

    return callback(null, data.ListBucketResult || {});
  });
}

/**
 * 创建 Bucket，并初始化访问权限
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
	 * @param  {string}   params.ACL     	用户自定义文件权限，可以设置：private，public-read；默认值：private，非必须
	 * @param  {string}   params.GrantRead     赋予被授权者读的权限，格式x-cos-grant-read: uin=" ",uin=" "，非必须
	 * @param  {string}   params.GrantWrite     赋予被授权者写的权限，格式x-cos-grant-write: uin=" ",uin=" "，非必须
	 * @param  {string}   params.GrantFullControl     赋予被授权者读写权限，格式x-cos-grant-full-control: uin=" ",uin=" "，非必须
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	 请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data  返回的数据
	 * @return  {string}	data.Location  操作地址
 */
function putBucket(params, callback) {
  const headers = {};
  headers['x-cos-acl'] = params.ACL;
  headers['x-cos-grant-read'] = params.GrantRead;
  headers['x-cos-grant-write'] = params.GrantWrite;
  headers['x-cos-grant-full-control'] = params.GrantFullControl;


  return submitRequest({
    method: 'PUT',
    Bucket: params.Bucket,
    Region: params.Region,
    headers,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      return callback(err);
    }

    data = data || {};

    return callback(null, {
      Location: getUrl({
        bucket: params.Bucket,
        region: params.Region,
        appid: params.Appid || config.APPID,
      }),
    });
  });
}

/**
 * 删除 Bucket
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	 请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data  返回的数据
	 * @return  {string}	data.Location  操作地址
 */
function deleteBucket(params, callback) {
  return submitRequest({
    method: 'DELETE',
    Bucket: params.Bucket,
    Region: params.Region,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      if (err.statusCode && err.statusCode == 204) {
        return callback(null, {
          DeleteBucketSuccess: true,
        });
      }
      return callback(err);
    }

    return callback(null, {
      DeleteBucketSuccess: true,
    });
  });
}

/**
 * 获取 Bucket 的 权限列表
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	 请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data  返回的数据
	 * @return  {object}	data.AccessControlPolicy  访问权限信息
 */
function getBucketACL(params, callback) {

  return submitRequest({
    method: 'GET',
    Bucket: params.Bucket,
    Region: params.Region,
    action: '/?acl',
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      return callback(err);
    }

    data = data || {};

    let Grant = data.AccessControlPolicy.AccessControlList.Grant || [];

    if (!(Grant instanceof Array)) {
      Grant = [ Grant ];
    }

    data.AccessControlPolicy.AccessControlList.Grant = Grant;

    return callback(null, data.AccessControlPolicy || {});
  });
}

/**
 * 设置 Bucket 的 权限列表
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
	 * @param  {string}   params.ACL     	用户自定义文件权限，可以设置：private，public-read；默认值：private，非必须
	 * @param  {string}   params.GrantRead     赋予被授权者读的权限，格式x-cos-grant-read: uin=" ",uin=" "，非必须
	 * @param  {string}   params.GrantWrite     赋予被授权者写的权限，格式x-cos-grant-write: uin=" ",uin=" "，非必须
	 * @param  {string}   params.GrantFullControl     赋予被授权者读写权限，格式x-cos-grant-full-control: uin=" ",uin=" "，非必须
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	 	请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data 	返回的数据
 */
function putBucketACL(params, callback) {
  const headers = {};

  headers['x-cos-acl'] = params.ACL;
  headers['x-cos-grant-read'] = params.GrantRead;
  headers['x-cos-grant-write'] = params.GrantWrite;
  headers['x-cos-grant-full-control'] = params.GrantFullControl;


  return submitRequest({
    method: 'PUT',
    Bucket: params.Bucket,
    Region: params.Region,
    action: '/?acl',
    headers,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      return callback(err);
    }

    return callback(null, {
      BucketGrantSuccess: true,
    });
  });
}

/**
 * 获取 Bucket 的 跨域设置
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	 	请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data 	返回的数据
	 * @return  {object}	data.CORSConfiguration 	 Bucket的跨域设置
 */
function getBucketCORS(params, callback) {
  return submitRequest({
    method: 'GET',
    Bucket: params.Bucket,
    Region: params.Region,
    action: '/?cors',
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      return callback(err);
    }

    data = data || {};

    data.CORSConfiguration = data.CORSConfiguration || {};

    let CORSRule = data.CORSConfiguration.CORSRule || [];


    if (!(CORSRule instanceof Array)) {
      CORSRule = [ CORSRule ];
    }

    for (let i = 0, len = CORSRule.length; i < len; i++) {
      const item = CORSRule[i];
      let AllowedHeader = item.AllowedHeader;
      let AllowedMethod = item.AllowedMethod;
      let AllowedOrigin = item.AllowedOrigin;
      let ExposeHeader = item.ExposeHeader;

      if (AllowedOrigin && !(AllowedOrigin instanceof Array)) {
        AllowedOrigin = [ AllowedOrigin ];
        item.AllowedOrigin = AllowedOrigin;
      }

      if (AllowedMethod && !(AllowedMethod instanceof Array)) {
        AllowedMethod = [ AllowedMethod ];
        item.AllowedMethod = AllowedMethod;
      }

      if (ExposeHeader && !(ExposeHeader instanceof Array)) {
        ExposeHeader = [ ExposeHeader ];
        item.ExposeHeader = ExposeHeader;
      }

      if (AllowedHeader && !(AllowedHeader instanceof Array)) {
        AllowedHeader = [ AllowedHeader ];
        item.AllowedHeader = AllowedHeader;
      }

    }

    data.CORSConfiguration.CORSRule = CORSRule;

    return callback(null, data.CORSConfiguration || {});
  });
}

/**
 * 设置 Bucket 的 跨域设置
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
	 * @param  {object}   params.CORSConfiguration		相关的跨域设置，必须
	 * @param  {array}	  params.CORSConfiguration.CORSRules	对应的跨域规则
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	   请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data   返回的数据
 */
function putBucketCORS(params, callback) {
  const headers = {};

  headers['Content-Type'] = 'application/xml';

  // 将 obj 中的 keys 转为 key
  const keys2key = function(keysObj, ignoreKeys) {
    ignoreKeys = ignoreKeys || [];
    const keyObj = {};
    for (const key in keysObj) {
      const lastChart = key.charAt(key.length - 1) || '';
      if (lastChart == 's' && ignoreKeys.indexOf(key) == -1) {
        keyObj[key.slice(0, -1)] = keysObj[key];
      } else {
        keyObj[key] = keysObj[key];
      }
    }

    return keyObj;
  };

  const CORSRules = params.CORSRules;

  const CORSRule = [];

  for (let i = 0, len = CORSRules.length; i < len; i++) {
    const item = CORSRules[i];
    CORSRule.push(keys2key(item, [ 'MaxAgeSeconds' ]));
  }

  const CORSConfiguration = {
    CORSConfiguration: {
      CORSRule,
    },
  };

  const xml = util.json2xml(CORSConfiguration);

  // headers['Content-MD5'] = util.md5(xml);


  return submitRequest({
    method: 'PUT',
    Bucket: params.Bucket,
    Region: params.Region,
    body: xml,
    action: '/?cors',
    headers,
    needHeaders: true,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      return callback(err);
    }

    return callback(null, {
      PutBucketCorsSucesss: true,
    });
  });
}


function putBucketPolicy(params, callback) {
  const headers = {};
  const Policy = params.Policy;
  const jsonPolicy = JSON.stringify(Policy);

  headers['Content-Type'] = 'application/json';
  headers['Content-MD5'] = util.md5(jsonPolicy);

  return submitRequest({
    method: 'PUT',
    Bucket: params.Bucket,
    Region: params.Region,
    action: '/?policy',
    body: Policy,
    headers,
    json: true,
    needHeaders: true,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {

      if (err.statusCode && err.statusCode == 403) {
        return callback({
          ErrorStatus: 'Access Denied',
        });
      }

      if (err.statusCode && err.statusCode == 405) {
        return callback({
          ErrorStatus: 'Method Not Allowed',
        });
      }

      if (err.statusCode && err.statusCode == 204) {
        return callback(null, {
          BucketPolicySuccess: true,
        });
      }

      return callback(err);
    }

    return callback(null, {
      BucketPolicySuccess: true,
    });
  });
}

/**
 * 删除 Bucket 的 跨域设置
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	   请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data   返回的数据
 */
function deleteBucketCORS(params, callback) {
  return submitRequest({
    method: 'DELETE',
    Bucket: params.Bucket,
    Region: params.Region,
    action: '/?cors',
    needHeaders: true,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      if (err.statusCode && err.statusCode == 204) {
        return callback(null, {
          DeleteBucketCorsSuccess: true,
        });
      }
      return callback(err);
    }

    return callback(null, {
      DeleteBucketCorsSuccess: true,
    });
  });
}

/**
 * 获取 Bucket 的 地域信息
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	   请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data   返回数据，包含地域信息 LocationConstraint
 */
function getBucketLocation(params, callback) {
  return submitRequest({
    method: 'GET',
    Bucket: params.Bucket,
    Region: params.Region,
    action: '/?location',
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      return callback(err);
    }

    return callback(null, data || {});
  });
}

/**
 * 获取 Bucket 的读取权限策略
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	   请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data   返回数据
 */
function getBucketPolicy(params, callback) {
  return submitRequest({
    method: 'GET',
    Bucket: params.Bucket,
    Region: params.Region,
    action: '/?policy',
    rawBody: true,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      if (err.statusCode && err.statusCode == 403) {
        return callback({
          ErrorStatus: 'Access Denied',
        });
      }

      if (err.statusCode && err.statusCode == 405) {
        return callback({
          ErrorStatus: 'Method Not Allowed',
        });
      }

      if (err.statusCode && err.statusCode == 404) {
        return callback({
          ErrorStatus: 'Policy Not Found',
        });
      }

      return callback(err);
    }

    data = data || {};

    try {
      data = JSON.parse(data.body);
    } catch (e) {

    }

    return callback(null, data || {});
  });
}


/**
 * 获取 Bucket 的标签设置
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	   请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data   返回数据
 */
function getBucketTagging(params, callback) {
  return submitRequest({
    method: 'GET',
    Bucket: params.Bucket,
    Region: params.Region,
    action: '/?tagging',
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      return callback(err);
    }

    data = data || {};

    let Tag = [];

    if (data && data.Tagging && data.Tagging.TagSet && data.Tagging.TagSet.Tag) {
      Tag = data.Tagging.TagSet.Tag;
    }

    if (!(Tag instanceof Array)) {
      Tag = [ Tag ];
    }

    data.Tagging.TagSet.Tag = Tag;

    return callback(null, {
      Tags: Tag,
    });
  });
}

/**
 * 设置 Bucket 的标签
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
	 * @param  {Array}   params.TagSet     标签设置，必须
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	   请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data   返回数据
 */
function putBucketTagging(params, callback) {
  const headers = {};

  headers['Content-Type'] = 'application/xml';

  const Tags = params.Tags;

  const TagSetting = {
    Tagging: {
      TagSet: {
        Tag: Tags,
      },
    },
  };

  const xml = util.json2xml(TagSetting);

  return submitRequest({
    method: 'PUT',
    Bucket: params.Bucket,
    Region: params.Region,
    body: xml,
    action: '/?tagging',
    headers,
    needHeaders: true,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      if (err.statusCode && err.statusCode == 204) {
        return callback(null, {
          PutBucketTaggingSuccess: true,
        });
      }
      return callback(err);
    }

    return callback(null, {
      PutBucketTaggingSuccess: true,
    });
  });
}


/**
 * 删除 Bucket 的 标签设置
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	   请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data   返回的数据
 */
function deleteBucketTagging(params, callback) {
  return submitRequest({
    method: 'DELETE',
    Bucket: params.Bucket,
    Region: params.Region,
    action: '/?tagging',
    needHeaders: true,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      if (err.statusCode && err.statusCode == 204) {
        return callback(null, {
          DeleteBucketTaggingSuccess: true,
        });
      }
      return callback(err);
    }

    return callback(null, {
      DeleteBucketTaggingSuccess: true,
    });
  });
}

// ----------------------------------------------------- Lifecycle 暂时尚未测通 -------------------------------------------------------

function getBucketLifecycle(params, callback) {
  return submitRequest({
    method: 'GET',
    Bucket: params.Bucket,
    Region: params.Region,
    action: '/?lifecycle',
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      return callback(err);
    }


    return callback(null, data || {});
  });
}

function putBucketLifecycle(params, callback) {
  const headers = {};

  headers['Content-Type'] = 'application/xml';

  const Rules = params.Rules;

  const Lifecycle = {
    LifecycleConfiguration: {
      Rule: Rules,
    },
  };

  const xml = util.json2xml(Lifecycle);


  return submitRequest({
    method: 'PUT',
    Bucket: params.Bucket,
    Region: params.Region,
    body: xml,
    action: '/?lifecycle',
    headers,
    needHeaders: true,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      if (err.statusCode && err.statusCode == 204) {
        return callback(null, {
          PutBucketLifecycleSuccess: true,
        });
      }
      return callback(err);
    }

    return callback(null, {
      PutBucketLifecycleSuccess: true,
    });
  });
}

function deleteBucketLifecycle(params, callback) {
  return submitRequest({
    method: 'DELETE',
    Bucket: params.Bucket,
    Region: params.Region,
    action: '/?lifecycle',
    needHeaders: true,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      if (err.statusCode && err.statusCode == 204) {
        return callback(null, {
          DeleteBucketLifecycleSuccess: true,
        });
      }
      return callback(err);
    }

    return callback(null, {
      DeleteBucketLifecycleSuccess: true,
    });
  });
}

// ------------------------------------------------------- Lifecycle 暂时尚未测通 -------------------------------------------------------------------


// ---------------------------------------- Object 相关 api ------------------------------------

/**
 * 取回对应Object的元数据，Head的权限与Get的权限一致
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
	 * @param  {string}   params.Key     文件名称，必须
	 * @param  {string}   params.IfModifiedSince   当Object在指定时间后被修改，则返回对应Object元信息，否则返回304，非必须
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	   请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data   为指定 object 的元数据，如果设置了 IfModifiedSince ，且文件未修改，则返回一个对象，NotModified 属性为 true
	 * @return  {Boolean}	data.NotModified   是否在 IfModifiedSince 时间点之后未修改该 object，则为 true
 */
function headObject(params, callback) {
  const headers = {};
  headers['If-Modified-Since'] = params.IfModifiedSince;

  return submitRequest({
    method: 'HEAD',
    Bucket: params.Bucket,
    Region: params.Region,
    Key: params.Key,
    headers,
    needHeaders: true,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      const statusCode = err.statusCode;
      if (headers['If-Modified-Since'] && statusCode && statusCode == 304) {
        return callback(null, {
          NotModified: true,
        });
      }
      return callback(err);
    }

    data = data || {};

    return callback(null, data.headers || {});
  });
}

/**
 * 下载 object
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
	 * @param  {string}   params.Key     文件名称，必须
	 * @param  {string || writeStream}   params.Output     文件输出地址或者写流，非必须
	 * @param  {string}   params.IfModifiedSince     当Object在指定时间后被修改，则返回对应Object元信息，否则返回304，非必须
	 * @param  {string}   params.IfUnmodifiedSince   如果文件修改时间早于或等于指定时间，才返回文件内容。否则返回 412 (precondition failed)，非必须
	 * @param  {string}   params.IfMatch             当 ETag 与指定的内容一致，才返回文件。否则返回 412 (precondition failed)，非必须
	 * @param  {string}   params.IfNoneMatch         当 ETag 与指定的内容不一致，才返回文件。否则返回304 (not modified)，非必须
	 * @param  {string}   params.ResponseContentType         设置返回头部中的 Content-Type 参数，非必须
	 * @param  {string}   params.ResponseContentLanguage         设置返回头部中的 Content-Language 参数，非必须
	 * @param  {string}   params.ResponseExpires         设置返回头部中的 Content-Expires 参数，非必须
	 * @param  {string}   params.ResponseCacheControl         设置返回头部中的 Cache-Control 参数，非必须
	 * @param  {string}   params.ResponseContentDisposition         设置返回头部中的 Content-Disposition 参数，非必须
	 * @param  {string}   params.ResponseContentEncoding         设置返回头部中的 Content-Encoding 参数，非必须
 * @param  {function}   callback      回调函数，必须
	 * @param  {object} 	err	 请求失败的错误，如果请求成功，则为空。
	 * @param  {object}		data 为对应的 object 数据，包括 body 和 headers
*/
function getObject(params, callback) {
  const headers = {};
  const reqParams = {};

  headers.Range = params.Range;
  headers['If-Modified-Since'] = params.IfModifiedSince;
  headers['If-Unmodified-Since'] = params.IfUnmodifiedSince;
  headers['If-Match'] = params.IfMatch;
  headers['If-None-Match'] = params.IfNoneMatch;

  reqParams['response-content-type'] = params.ResponseContentType;
  reqParams['response-content-language'] = params.ResponseContentLanguage;
  reqParams['response-expires'] = params.ResponseExpires;
  reqParams['response-cache-control'] = params.ResponseCacheControl;
  reqParams['response-content-disposition'] = params.ResponseContentDisposition;
  reqParams['response-content-encoding'] = params.ResponseContentEncoding;

  const output = params.Output;

  let writeStream = output;

  if (output && (typeof output === 'string')) {
    writeStream = fs.createWriteStream(output);
  }

  // 如果用户自己传入了 output
  if (output) {
    return submitRequest({
      method: 'GET',
      Bucket: params.Bucket,
      Region: params.Region,
      Key: params.Key,
      headers,
      qs: reqParams,
      needHeaders: true,
      rawBody: true,
      Appid: params.Appid,
      SecretId: params.SecretId,
      SecretKey: params.SecretKey,
    }, function(err, data) {
      if (err) {
        const statusCode = err.statusCode;
        if (headers['If-Modified-Since'] && statusCode && statusCode == 304) {
          return callback(null, {
            NotModified: true,
          });
        }
        return callback(err);
      }

      data = data || {};

      return callback(null, data.headers || {});
    }).pipe(writeStream);
  }

  // 如果用户不传入 output
  return submitRequest({
    method: 'GET',
    Bucket: params.Bucket,
    Region: params.Region,
    Key: params.Key,
    headers,
    qs: reqParams,
    needHeaders: true,
    rawBody: true,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      const statusCode = err.statusCode;
      if (headers['If-Modified-Since'] && statusCode && statusCode == 304) {
        return callback(null, {
          NotModified: true,
        });
      }
      return callback(err);
    }

    data = data || {};

    return callback(null, data.headers || {});
  });
}

/**
 * 上传 object
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
	 * @param  {string}   params.Key     文件名称，必须
	 * @param  {string || readStream}   params.Body     要上传的文件地址或者读流，必须
	 * @param  {string}   params.CacheControl    	 RFC 2616 中定义的缓存策略，将作为 Object 元数据保存，非必须
	 * @param  {string}   params.ContentDisposition   RFC 2616 中定义的文件名称，将作为 Object 元数据保存，非必须
	 * @param  {string}   params.ContentEncoding             RFC 2616 中定义的编码格式，将作为 Object 元数据保存，非必须
	 * @param  {string}   params.ContentLength         		 RFC 2616 中定义的 HTTP 请求内容长度（字节），必须
	 * @param  {string}   params.ContentType         RFC 2616 中定义的内容类型（MIME），将作为 Object 元数据保存，非必须
	 * @param  {string}   params.Expect         当使用 Expect: 100-continue 时，在收到服务端确认后，才会发送请求内容，非必须
	 * @param  {string}   params.Expires         RFC 2616 中定义的过期时间，将作为 Object 元数据保存，非必须
	 * @param  {string}   params.ContentSha1         RFC 3174 中定义的 160-bit 内容 SHA-1 算法校验，非必须
	 * @param  {string}   params.ACL         允许用户自定义文件权限，有效值：private | public-read，非必须
	 * @param  {string}   params.GrantRead         赋予被授权者读的权限，格式 x-cos-grant-read: uin=" ",uin=" "，非必须
	 * @param  {string}   params.GrantWrite         赋予被授权者写的权限，格式 x-cos-grant-write: uin=" ",uin=" "，非必须
	 * @param  {string}   params.GrantFullControl         赋予被授权者读写权限，格式 x-cos-grant-full-control: uin=" ",uin=" "，非必须
 * @param  {function}   callback      	回调函数，必须
	 * @return  {object} 	err	 		请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data 		为对应的 object 数据
	 * @return  {string}	data.ETag 	为对应上传文件的 ETag 值
*/
function putObject(params, callback) {
  const headers = {};

  headers['Cache-Control'] = params.CacheControl;
  headers['Content-Disposition'] = params.ContentDisposition;
  headers['Content-Encoding'] = params.ContentEncoding;
  // headers['Cotent-MD5'] = params['CotentMD5'];
  headers['Content-Length'] = params.ContentLength;
  headers['Content-Type'] = params.ContentType;
  headers.Expect = params.Expect;
  headers.Expires = params.Expires;
  headers['x-cos-content-sha1'] = params.ContentSha1;
  headers['x-cos-acl'] = params.ACL;
  headers['x-cos-grant-read'] = params.GrantRead;
  headers['x-cos-grant-write'] = params.GrantWrite;
  headers['x-cos-grant-full-control'] = params.GrantFullControl;
  headers['x-cos-storage-class'] = params.StorageClass;

  for (const key in params) {
    if (key.indexOf('x-cos-meta-') > -1) {
      headers[key] = params[key];
    }
  }

  const body = params.Body;

  let readStream = body;

  if (body && (typeof body === 'string')) {
    readStream = fs.createReadStream(body);
  }

  return submitRequest({
    method: 'PUT',
    Bucket: params.Bucket,
    Region: params.Region,
    Key: params.Key,
    headers,
    body: readStream,
    needHeaders: true,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      return callback(err);
    }

    data = data || {};

    if (data && data.headers && data.headers.etag) {
      return callback(null, {
        ETag: data.headers.etag,
      });
    }

    return callback(null, data);
  });
}

/**
 * 删除 object
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
	 * @param  {string}   params.Key     object名称，必须
 * @param  {function}   callback      回调函数，必须
	 * @param  {object} 	err	 请求失败的错误，如果请求成功，则为空。
	 * @param  {object}		data  删除操作成功之后返回的数据，如果删除操作成功，则返回 success 为 true, 并且附带原先 object 的 url
	 * @param  {Boolean}	data.Success  删除操作是否成功，成功则为 true，否则为 false
	 * @param  {Boolean}	data.BucketNotFound      请求的 object 所在的 bucket 是否不存在，如果为 true，则说明该 bucket 不存在
 */
function deleteObject(params, callback) {
  submitRequest({
    method: 'DELETE',
    Bucket: params.Bucket,
    Region: params.Region,
    Key: params.Key,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      const statusCode = err.statusCode;
      if (statusCode && statusCode == 204) {
        return callback(null, {
          DeleteObjectSuccess: true,
        });
      } else if (statusCode && statusCode == 404) {
        return callback(null, {
          BucketNotFound: true,
        });
      }
      return callback(err);

    }

    return callback(null, {
      DeleteObjectSuccess: true,
    });

  });
}

/**
 * 获取 object 的 权限列表
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
	 * @param  {string}   params.Key     object名称，必须
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	 请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data 返回的数据
	 * @return  {object}	data.AccessControlPolicy  权限列表
 */
function getObjectACL(params, callback) {

  return submitRequest({
    method: 'GET',
    Bucket: params.Bucket,
    Region: params.Region,
    Key: params.Key,
    action: '?acl',
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      return callback(err);
    }

    data = data || {};

    let Grant = data.AccessControlPolicy.AccessControlList.Grant || [];

    if (!(Grant instanceof Array)) {
      Grant = [ Grant ];
    }

    data.AccessControlPolicy.AccessControlList.Grant = Grant;

    return callback(null, data.AccessControlPolicy || {});
  });
}

/**
 * 设置 object 的 权限列表
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
	 * @param  {string}   params.Key     object名称，必须
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	 请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data 返回的数据
 */
function putObjectACL(params, callback) {
  const headers = {};

  headers['x-cos-acl'] = params.ACL;
  headers['x-cos-grant-read'] = params.GrantRead;
  headers['x-cos-grant-write'] = params.GrantWrite;
  headers['x-cos-grant-full-control'] = params.GrantFullControl;

  return submitRequest({
    method: 'PUT',
    Bucket: params.Bucket,
    Region: params.Region,
    Key: params.Key,
    action: '?acl',
    headers,
    needHeaders: true,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      return callback(err);
    }

    return callback(null, {
      PutObjectACLSuccess: true,
    });
  });
}

/**
 * Options Object请求实现跨域访问的预请求。即发出一个 OPTIONS 请求给服务器以确认是否可以进行跨域操作。
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
	 * @param  {string}   params.Key     object名称，必须
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	 请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data 返回的数据
 */
function optionsObject(params, callback) {
  const headers = {};

  headers.Origin = params.Origin;
  headers['Access-Control-Request-Method'] = params.AccessControlRequestMethod;
  headers['Access-Control-Request-Headers'] = params.AccessControlRequestHeaders;

  return submitRequest({
    method: 'OPTIONS',
    Bucket: params.Bucket,
    Region: params.Region,
    Key: params.Key,
    headers,
    needHeaders: true,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      if (err.statusCode && err.statusCode == 403) {
        return callback(null, {
          OptionsForbidden: true,
        });
      }
      return callback(err);
    }

    data = data || {};

    const resHeaders = data.headers || {};

    const retData = {};

    retData.AccessControlAllowOrigin = resHeaders['access-control-allow-origin'];
    retData.AccessControlAllowMethods = resHeaders['access-control-allow-methods'];
    retData.AccessControlAllowHeaders = resHeaders['access-control-allow-headers'];
    retData.AccessControlExposeHeaders = resHeaders['access-control-expose-headers'];
    retData.AccessControlMaxAge = resHeaders['access-control-max-age'];

    return callback(null, retData);
  });
}

/**
 * @params** (Object) ： 参数列表
	* Bucket —— (String) ： Bucket 名称
	* Region —— (String) ： 地域名称
	* Key —— (String) ： 文件名称
	* CopySource —— (String) ： 源文件URL绝对路径，可以通过versionid子资源指定历史版本
	* ACL —— (String)  ： 允许用户自定义文件权限。有效值：private，public-read默认值：private。
	* GrantRead —— (String)  ： 赋予被授权者读的权限，格式 x-cos-grant-read: uin=" ",uin=" "，当需要给子账户授权时，uin="RootAcountID/SubAccountID"，当需要给根账户授权时，uin="RootAcountID"。
	* GrantWrite —— (String)  ： 赋予被授权者写的权限，格式 x-cos-grant-write: uin=" ",uin=" "，当需要给子账户授权时，uin="RootAcountID/SubAccountID"，当需要给根账户授权时，uin="RootAcountID"。
	* GrantFullControl —— (String)  ： 赋予被授权者读写权限，格式 x-cos-grant-full-control: uin=" ",uin=" "，当需要给子账户授权时，uin="RootAcountID/SubAccountID"，当需要给根账户授权时，uin="RootAcountID"。
	* MetadataDirective —— (String) ： 是否拷贝元数据，枚举值：Copy, Replaced，默认值Copy。假如标记为Copy，忽略Header中的用户元数据信息直接复制；假如标记为Replaced，按Header信息修改元数据。当目标路径和原路径一致，即用户试图修改元数据时，必须为Replaced
	* CopySourceIfModifiedSince —— (String) ： 当Object在指定时间后被修改，则执行操作，否则返回412。可与x-cos-copy-source-If-None-Match一起使用，与其他条件联合使用返回冲突。
	* CopySourceIfUnmodifiedSince —— (String) ： 当Object在指定时间后未被修改，则执行操作，否则返回412。可与x-cos-copy-source-If-Match一起使用，与其他条件联合使用返回冲突。
	* CopySourceIfMatch —— (String) ： 当Object的Etag和给定一致时，则执行操作，否则返回412。可与x-cos-copy-source-If-Unmodified-Since一起使用，与其他条件联合使用返回冲突。
	* CopySourceIfNoneMatch —— (String) ： 当Object的Etag和给定不一致时，则执行操作，否则返回412。可与x-cos-copy-source-If-Modified-Since一起使用，与其他条件联合使用返回冲突。
	* StorageClass —— (String) ： 存储级别，枚举值：存储级别，枚举值：Standard, Standard_IA，Nearline；默认值：Standard
	* CacheControl —— (String) ： 指定所有缓存机制在整个请求/响应链中必须服从的指令。
	* ContentDisposition —— (String) ： MIME 协议的扩展，MIME 协议指示 MIME 用户代理如何显示附加的文件
	* ContentEncoding —— (String) ： HTTP 中用来对「采用何种编码格式传输正文」进行协定的一对头部字段
	* ContentLength —— (String) ： 设置响应消息的实体内容的大小，单位为字节
	* ContentType —— (String) ： RFC 2616 中定义的 HTTP 请求内容类型（MIME），例如text/plain
	* Expect —— (String) ： 请求的特定的服务器行为
	* Expires —— (String) ： 	响应过期的日期和时间
	* ContentLanguage —— (String) ： 指定内容语言
	* x-cos-meta-* —— (String) ： 允许用户自定义的头部信息，将作为 Object 元数据返回。大小限制2K。
*/
function putObjectCopy(params, callback) {
  const headers = {};

  headers['x-cos-copy-source'] = params.CopySource;
  headers['x-cos-metadata-directive'] = params.MetadataDirective;
  headers['x-cos-copy-source-If-Modified-Since'] = params.CopySourceIfModifiedSince;
  headers['x-cos-copy-source-If-Unmodified-Since'] = params.CopySourceIfUnmodifiedSince;
  headers['x-cos-copy-source-If-Match'] = params.CopySourceIfMatch;
  headers['x-cos-copy-source-If-None-Match'] = params.CopySourceIfNoneMatch;
  headers['x-cos-storage-class'] = params.StorageClass;
  headers['x-cos-acl'] = params.ACL;
  headers['x-cos-grant-read'] = params.GrantRead;
  headers['x-cos-grant-write'] = params.GrantWrite;
  headers['x-cos-grant-full-control'] = params.GrantFullControl;
  headers['Cache-Control'] = params.CacheControl;
  headers['Content-Disposition'] = params.ContentDisposition;
  headers['Content-Encoding'] = params.ContentEncoding;
  headers['Content-Length'] = params.ContentLength;
  headers['Content-Type'] = params.ContentType;
  headers.Expect = params.Expect;
  headers.Expires = params.Expires;
  headers['x-cos-content-sha1'] = params.ContentSha1;

  for (const key in params) {
    if (key.indexOf('x-cos-meta-') > -1) {
      headers[key] = params[key];
    }
  }

  return submitRequest({
    method: 'PUT',
    Bucket: params.Bucket,
    Region: params.Region,
    Key: params.Key,
    headers,
    needHeaders: true,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      return callback(err);
    }

    data = data || {};

    return callback(null, data.CopyObjectResult || {});
  });
}


function deleteMultipleObject(params, callback) {
  const headers = {};

  headers['Content-Type'] = 'application/xml';

  const Objects = params.Objects || {};
  const Quiet = params.Quiet;

  const DeleteConfiguration = {
    Delete: {
      Object: Objects,
      Quiet: Quiet || false,
    },
  };

  const xml = util.json2xml(DeleteConfiguration);

  headers['Content-MD5'] = util.md5(xml);
  headers['Content-Length'] = Buffer.byteLength(xml, 'utf8');

  return submitRequest({
    method: 'POST',
    Bucket: params.Bucket,
    Region: params.Region,
    body: xml,
    action: '/?delete',
    headers,
    needHeaders: true,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      return callback(err);
    }

    data = data || {};

    let Deleted = data.DeleteResult.Deleted || [];
    let Errors = data.DeleteResult.Error || [];

    if (!(Deleted instanceof Array)) {
      Deleted = [ Deleted ];
    }

    if (!(Errors instanceof Array)) {
      Errors = [ Errors ];
    }

    data.DeleteResult.Error = Errors;
    data.DeleteResult.Deleted = Deleted;

    return callback(null, data.DeleteResult || {});
  });
}


// ----------------------------------------- 分块上传相关部分 ----------------------------------


/**
 * 初始化分块上传
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
	 * @param  {string}   params.Key     object名称，必须
	 * @param  {string}	  params.CacheControl 	RFC 2616 中定义的缓存策略，将作为 Object 元数据保存，非必须
	 * @param  {string}	  params.ContentDisposition 	RFC 2616 中定义的文件名称，将作为 Object 元数据保存	，非必须
	 * @param  {string}	  params.ContentEncoding 		RFC 2616 中定义的编码格式，将作为 Object 元数据保存，非必须
	 * @param  {string}	  params.ContentType 	RFC 2616 中定义的内容类型（MIME），将作为 Object 元数据保存，非必须
	 * @param  {string}	  params.Expires 	RFC 2616 中定义的过期时间，将作为 Object 元数据保存，非必须
	 * @param  {string}	  params.ACL 		允许用户自定义文件权限，非必须
	 * @param  {string}	  params.GrantRead 	赋予被授权者读的权限 ，非必须
	 * @param  {string}	  params.GrantWrite 	赋予被授权者写的权限 ，非必须
	 * @param  {string}	  params.GrantFullControl 	赋予被授权者读写权限 ，非必须
	 * @param  {string}	  params.StorageClass 	设置Object的存储级别，枚举值：Standard，Standard_IA，Nearline，非必须
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	 请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data 返回的数据
	 * @return  {object} 	data.InitiateMultipartUploadResult  初始化上传信息，包括 Bucket(Bucket名称), Key(文件名称) 和 UploadId (上传任务ID)
 */
function MultipartInit(params, callback) {
  const headers = {};

  headers['Cache-Control'] = params.CacheControl;
  headers['Content-Disposition'] = params.ContentDisposition;
  headers['Content-Encoding'] = params.ContentEncoding;
  headers['Content-Type'] = params.ContentType;
  headers.Expires = params.Expires;

  headers['x-cos-acl'] = params.ACL;
  headers['x-cos-grant-read'] = params.GrantRead;
  headers['x-cos-grant-write'] = params.GrantWrite;
  headers['x-cos-grant-full-control'] = params.GrantFullControl;
  headers['x-cos-storage-class'] = params.StorageClass;

  for (const key in params) {
    if (key.indexOf('x-cos-meta-') > -1) {
      headers[key] = params[key];
    }
  }

  return submitRequest({
    method: 'POST',
    Bucket: params.Bucket,
    Region: params.Region,
    Key: params.Key,
    action: '?uploads',
    headers,
    needHeaders: true,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      return callback(err);
    }

    data = data || {};

    if (data && data.InitiateMultipartUploadResult) {
      return callback(null, data.InitiateMultipartUploadResult);
    }

    return callback(null, data);


  });
}

/**
 *	分块上传
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
	 * @param  {string}   params.Key     object名称，必须
	 * @param  {string}	  params.ContentLength 		RFC 2616 中定义的 HTTP 请求内容长度（字节），非必须
	 * @param  {string}	  params.Expect 		当使用 Expect: 100-continue 时，在收到服务端确认后，才会发送请求内容，非必须
	 * @param  {string}	  params.ContentSha1 	RFC 3174 中定义的 160-bit 内容 SHA-1 算法校验值，非必须
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	 请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data 返回的数据
	 * @return  {object} 	data.ETag  返回的文件分块 sha1 值
 */
function MultipartUpload(params, callback) {
  const headers = {};

  headers['Content-Length'] = params.ContentLength;
  headers.Expect = params.Expect;
  headers['x-cos-content-sha1'] = params.ContentSha1;

  const PartNumber = params.PartNumber;
  const UploadId = params.UploadId;

  const action = '?partNumber=' + PartNumber + '&uploadId=' + UploadId;

  const body = params.Body;

  const req = submitRequest({
    method: 'PUT',
    Bucket: params.Bucket,
    Region: params.Region,
    Key: params.Key,
    action,
    headers,
    needHeaders: true,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      return callback(err);
    }

    data = data || {};

    data.headers = data.headers || {};

    return callback(null, {
      ETag: data.headers.etag || '',
    });

  });

  return body.pipe(req);
}

/**
 *	完成分块上传
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
	 * @param  {string}   params.Key     object名称，必须
	 * @param  {array}	  params.Parts 	  	分块信息列表，必须
	 * @param  {string}	  params.Parts[i].PartNumber 	块编号，必须
	 * @param  {string}   params.Parts[i].ETag 		分块的 sha1 校验值
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	 请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data 返回的数据
	 * @return  {object} 	data.CompleteMultipartUpload   完成分块上传后的文件信息，包括Location, Bucket, Key 和 ETag
 */
function MultipartComplete(params, callback) {
  const headers = {};

  headers['Content-Type'] = 'application/xml';

  const UploadId = params.UploadId;

  const action = '?uploadId=' + UploadId;

  const Parts = params.Parts;

  for (let i = 0, len = Parts.length; i < len; i++) {
    if (Parts[i].ETag.indexOf('"') == 0) {
      continue;
    }
    Parts[i].ETag = '"' + Parts[i].ETag + '"';
  }

  const PartData = {
    CompleteMultipartUpload: {
      Part: Parts,
    },
  };

  const xml = util.json2xml(PartData);

  headers['Content-length'] = Buffer.byteLength(xml, 'utf8');

  return submitRequest({
    method: 'POST',
    Bucket: params.Bucket,
    Region: params.Region,
    Key: params.Key,
    action,
    body: xml,
    headers,
    needHeaders: true,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      return callback(err);
    }

    data = data || {};

    return callback(null, data.CompleteMultipartUpload || {});
  });
}

/**
 *	分块上传任务列表查询
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
	 * @param  {string}   params.Delimiter     定界符为一个符号，如果有Prefix，则将Prefix到delimiter之间的相同路径归为一类，定义为Common Prefix，然后列出所有Common Prefix。如果没有Prefix，则从路径起点开始，非必须
	 * @param  {string}   params.EncodingType     规定返回值的编码方式，非必须
	 * @param  {string}   params.Prefix     前缀匹配，用来规定返回的文件前缀地址，非必须
	 * @param  {string}   params.MaxUploads     单次返回最大的条目数量，默认1000，非必须
	 * @param  {string}   params.KeyMarker     与upload-id-marker一起使用 </Br>当upload-id-marker未被指定时，ObjectName字母顺序大于key-marker的条目将被列出 </Br>当upload-id-marker被指定时，ObjectName字母顺序大于key-marker的条目被列出，ObjectName字母顺序等于key-marker同时UploadID大于upload-id-marker的条目将被列出，非必须
	 * @param  {string}   params.UploadIdMarker     与key-marker一起使用 </Br>当key-marker未被指定时，upload-id-marker将被忽略 </Br>当key-marker被指定时，ObjectName字母顺序大于key-marker的条目被列出，ObjectName字母顺序等于key-marker同时UploadID大于upload-id-marker的条目将被列出，非必须
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	 请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data 返回的数据
	 * @return  {object} 	data.ListMultipartUploadsResult   分块上传任务信息
 */
function MultipartList(params, callback) {
  let reqParams = {};

  reqParams.delimiter = params.Delimiter;
  reqParams['encoding-type'] = params.EncodingType;
  reqParams.prefix = params.Prefix;

  reqParams['max-uploads'] = params.MaxUploads;

  reqParams['key-marker'] = params.KeyMarker;
  reqParams['upload-id-marker'] = params.UploadIdMarker;

  reqParams = util.clearKey(reqParams);


  return submitRequest({
    method: 'GET',
    Bucket: params.Bucket,
    Region: params.Region,
    action: '/?uploads&' + querystring.stringify(reqParams),
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      return callback(err);
    }

    data = data || {};

    if (data && data.ListMultipartUploadsResult) {
      let Upload = data.ListMultipartUploadsResult.Upload || [];

      let CommonPrefixes = data.ListMultipartUploadsResult.CommonPrefixes || [];


      if (!(CommonPrefixes instanceof Array)) {
        CommonPrefixes = [ CommonPrefixes ];
      }

      if (!(Upload instanceof Array)) {
        Upload = [ Upload ];
      }

      data.ListMultipartUploadsResult.Upload = Upload;
      data.ListMultipartUploadsResult.CommonPrefixes = CommonPrefixes;
    }

    return callback(null, data.ListMultipartUploadsResult || {});
  });
}

/**
 *	上传的分块列表查询
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
	 * @param  {string}   params.Key     object名称，必须
	 * @param  {string}   params.UploadId     标示本次分块上传的ID，必须
	 * @param  {string}   params.EncodingType     规定返回值的编码方式，非必须
	 * @param  {string}   params.MaxParts     单次返回最大的条目数量，默认1000，非必须
	 * @param  {string}   params.PartNumberMarker     默认以UTF-8二进制顺序列出条目，所有列出条目从marker开始，非必须
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	 请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data 返回的数据
	 * @return  {object} 	data.ListMultipartUploadsResult   分块信息
 */
function MultipartListPart(params, callback) {
  const reqParams = {};

  reqParams.uploadId = params.UploadId;
  reqParams['encoding-type'] = params.EncodingType;
  reqParams['max-parts'] = params.MaxParts;
  reqParams['part-number-marker'] = params.PartNumberMarker;


  return submitRequest({
    method: 'GET',
    Bucket: params.Bucket,
    Region: params.Region,
    Key: params.Key,
    qs: reqParams,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      return callback(err);
    }

    data = data || {};

    let Part = data.ListPartsResult.Part || [];

    if (!(Part instanceof Array)) {
      Part = [ Part ];
    }

    data.ListPartsResult.Part = Part;

    return callback(null, data.ListPartsResult || {});
  });
}

/**
 *	抛弃分块上传
 * @param  {object}   params     参数对象，必须
	 * @param  {string}   params.Bucket     Bucket名称，必须
	 * @param  {string}   params.Region     地域名称，必须
	 * @param  {string}   params.Key     object名称，必须
	 * @param  {string}   params.UploadId     标示本次分块上传的ID，必须
 * @param  {function}   callback      回调函数，必须
	 * @return  {object} 	err	 请求失败的错误，如果请求成功，则为空。
	 * @return  {object}	data 返回的数据
 */
function MultipartAbort(params, callback) {
  const reqParams = {};

  reqParams.uploadId = params.UploadId;

  return submitRequest({
    method: 'DELETE',
    Bucket: params.Bucket,
    Region: params.Region,
    Key: params.Key,
    qs: reqParams,
    needHeaders: true,
    Appid: params.Appid,
    SecretId: params.SecretId,
    SecretKey: params.SecretKey,
  }, function(err, data) {
    if (err) {
      return callback(err);
    }

    return callback(null, {
      MultipartAbortSuccess: true,
    });
  });
}


/**
*	String 方法添加
*/

String.prototype.strip = function() {
  return this.replace(/(^\/*)|(\/*$)/g, '');
};

String.prototype.lstrip = function() {
  return this.replace(/(^\/*)/g, '');
};

String.prototype.rstrip = function() {
  return this.replace(/(\/*$)/g, '');
};

/**
*	私有方法
*/

// 生成操作 url
function getUrl(params) {
  const bucket = params.bucket;
  const region = params.region;
  const object = params.object;
  const action = params.action;
  const appid = params.appid;

  let url = 'http://' + bucket + '-' + appid + '.' + region + '.myqcloud.com';

  if (object) {
    url += '/' + encodeURIComponent(object);
  }

  if (action) {
    url += action;
  }

  return url;
}

// 检测参数是否填写完全
function checkParamsRequire(callerName, params) {
  const bucket = params.Bucket;
  const region = params.Region;
  const object = params.Key;

  if (callerName.indexOf('Bucket') > -1 || callerName == 'deleteMultipleObject' || callerName == 'MultipartList') {
    if (!bucket || !region) {
      return false;
    }

    return true;
  }

  if (callerName.indexOf('Object') > -1) {
    if (!bucket || !region || !object) {
      return false;
    }

    return true;
  }

  if (callerName.indexOf('Multipart') > -1) {
    if (!bucket || !region || !object) {
      return false;
    }

    return true;
  }

}

// 发起请求
function submitRequest(params, callback) {

  // 获取默认秘钥信息
  const defaultAuth = {
    Appid: config.APPID,
    SecretId: config.SECRET_ID,
    SecretKey: config.SECRET_KEY,
  };

  const bucket = params.Bucket;
  const region = params.Region;
  let object = params.Key;
  const action = params.action;
  const method = params.method || 'GET';
  const headers = params.headers || {};
  const url = params.url;
  const body = params.body;
  const json = params.json;

  // 通过调用的函数名确定需要的参数
  const callerName = arguments.callee.caller.name;


  if (!checkParamsRequire(callerName, params) && callerName !== 'getService') {
    return callback({
      error: 'lack of required params',
    });
  }

  const needHeaders = params.needHeaders;
  const rawBody = params.rawBody;

  const qs = params.qs;

  const appid = params.Appid || defaultAuth.Appid;
  const secretId = params.SecretId || defaultAuth.SecretId;
  const secretKey = params.SecretKey || defaultAuth.SecretKey;

  const opt = {
    url: url || getUrl({
      bucket,
      region,
      object,
      action,
      appid,
      secretId,
      secretKey,
    }),
    method,
    headers,
    qs,
    body,
    json,
    // 这里的 proxy 用于处理内网网关限制，代理转发华南园区的请求，华北园区无需代理
    // 'proxy':'http://dev-proxy.oa.com:8080'
  };


  if (object) {
    object = '/' + object;
  }

  // 获取签名
  opt.headers.Authorization = util.getAuth({
    method: opt.method,
    pathname: object || '/',
    appid,
    secretId,
    secretKey,
  });

  // 预先处理 undefine 的属性
  if (opt.headers) {
    opt.headers = util.clearKey(opt.headers);
  }

  if (opt.qs) {
    opt.qs = util.clearKey(opt.qs);
  }

  return REQUEST(opt, function(err, response, body) {

    // 请求错误，发生网络错误
    if (err) {
      return callback({
        error: err,
      });
    }

    const statusCode = response.statusCode;
    let jsonRes;

    try {
      jsonRes = util.xml2json(body) || {};
    } catch (e) {
      jsonRes = body || {};
    }

    // 请求返回码不为 200
    if (statusCode != 200) {
      return callback({
        statusCode,
        error: jsonRes.Error || jsonRes,
      });
    }

    // 不对 body 进行转换，body 直接挂载返回
    if (rawBody) {
      jsonRes = {};
      jsonRes.body = body;
    }

    // 如果需要头部信息，则 headers 挂载返回
    if (needHeaders) {
      jsonRes.headers = response.headers || {};
    }

    if (jsonRes.Error) {
      return callback({
        statusCode,
        error: jsonRes.Error,
      });
    }

    return callback(null, jsonRes);
  });
}


// bucket 相关
exports.getService = getService;
exports.getBucket = getBucket;
exports.headBucket = headBucket;
exports.putBucket = putBucket;
exports.deleteBucket = deleteBucket;
exports.getBucketACL = getBucketACL;
exports.putBucketACL = putBucketACL;
exports.getBucketCORS = getBucketCORS;
exports.putBucketCORS = putBucketCORS;
exports.deleteBucketCORS = deleteBucketCORS;
exports.getBucketLocation = getBucketLocation;
// exports.getBucketPolicy = getBucketPolicy;
// exports.putBucketPolicy = putBucketPolicy;
exports.getBucketTagging = getBucketTagging;
exports.putBucketTagging = putBucketTagging;
exports.deleteBucketTagging = deleteBucketTagging;
/*
exports.getBucketLifecycle = getBucketLifecycle;
exports.putBucketLifecycle = putBucketLifecycle;
exports.deleteBucketLifecycle = deleteBucketLifecycle;
*/

// object 相关
exports.getObject = getObject;
exports.headObject = headObject;
exports.putObject = putObject;
exports.deleteObject = deleteObject;
exports.getObjectACL = getObjectACL;
exports.putObjectACL = putObjectACL;
exports.optionsObject = optionsObject;
// exports.putObjectCopy = putObjectCopy;
exports.deleteMultipleObject = deleteMultipleObject;

// 分块上传相关
exports.MultipartInit = MultipartInit;
exports.MultipartUpload = MultipartUpload;
exports.MultipartComplete = MultipartComplete;
exports.MultipartList = MultipartList;
exports.MultipartListPart = MultipartListPart;
exports.MultipartAbort = MultipartAbort;
